﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

// Выводит подключенные команды в форме.
// Для вызова из обработчика формы ПриСозданииНаСервере.
//
// Если в форме несколько списков, то в обработчике формы ПриСозданииНаСервере
// следует разместить несколько вызовов этой процедуры с указанием параметра ПараметрыРазмещения.
// Параметр ПараметрыРазмещения также применяется, когда типы источников зависят от параметров открытия формы.
//
// Параметры:
//   Форма - ФормаКлиентскогоПриложения - форма, в которой необходимо разместить команды.
//   ПараметрыРазмещения - см. ПодключаемыеКоманды.ПараметрыРазмещения
//                       - Неопределено
//
Процедура ПриСозданииНаСервере(Форма, Знач ПараметрыРазмещения = Неопределено) Экспорт
	ИмяФормы = Форма.ИмяФормы;
	
	ПереданныеПараметрыРазмещения = ПараметрыРазмещения;
	
	ПараметрыРазмещения = ПараметрыРазмещения();
	Если ПереданныеПараметрыРазмещения <> Неопределено Тогда
		ЗаполнитьЗначенияСвойств(ПараметрыРазмещения, ПереданныеПараметрыРазмещения);
	КонецЕсли;
	
	ИсточникиЧерезЗапятую = "";
	
	Если ТипЗнч(ПараметрыРазмещения.Источники) = Тип("ОписаниеТипов") Тогда
		Типы = ПараметрыРазмещения.Источники.Типы();
		Для Каждого Тип Из Типы Цикл
			ОбъектМетаданных = Метаданные.НайтиПоТипу(Тип);
			Если ОбъектМетаданных <> Неопределено Тогда
				ИсточникиЧерезЗапятую = ИсточникиЧерезЗапятую + ?(ИсточникиЧерезЗапятую = "", "", ",") + ОбъектМетаданных.ПолноеИмя();
			КонецЕсли;
		КонецЦикла;
	ИначеЕсли ТипЗнч(ПараметрыРазмещения.Источники) = Тип("Массив") Тогда
		Для Каждого ОбъектМетаданных Из ПараметрыРазмещения.Источники Цикл
			Если ТипЗнч(ОбъектМетаданных) = Тип("ОбъектМетаданных") Тогда
				ИсточникиЧерезЗапятую = ИсточникиЧерезЗапятую + ?(ИсточникиЧерезЗапятую = "", "", ",") + ОбъектМетаданных.ПолноеИмя();
			ИначеЕсли ОбъектМетаданных <> Неопределено Тогда
				ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(
					"ПодключаемыеКоманды.ПриСозданииНаСервере",
					"ПараметрыРазмещения.Источники[...]",
					ОбъектМетаданных,
					Новый ОписаниеТипов("ОбъектМетаданных"));
			КонецЕсли;
		КонецЦикла;
	ИначеЕсли ПараметрыРазмещения.Источники <> Неопределено Тогда
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(
			"ПодключаемыеКоманды.ПриСозданииНаСервере",
			"ПараметрыРазмещения.Источники",
			ПараметрыРазмещения.Источники,
			Новый ОписаниеТипов("ОписаниеТипов, Массив"));
	КонецЕсли;
	
	ЭтоФормаОбъекта = Неопределено;
	Параметры = Форма.Параметры;
	ЕстьПараметрыСписка  = Параметры.Свойство("Отбор") И Параметры.Свойство("ТекущаяСтрока");
	ЕстьПараметрыОбъекта = Параметры.Свойство("Ключ")  И Параметры.Свойство("Основание");
	Если ЕстьПараметрыСписка <> ЕстьПараметрыОбъекта Тогда
		ЭтоФормаОбъекта = ЕстьПараметрыОбъекта;
	КонецЕсли;
	// Для форм внешних отчетов и обработок невозможно получить метаданные имея только имя формы,
	// поэтому определение источников команд выполняется до вызова ПовтИсп.
	Если ИсточникиЧерезЗапятую = "" И ТребуетсяУказаниеИсточниковКоманд(ИмяФормы) Тогда
		Если ЕстьПараметрыОбъекта Тогда
			ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(Параметры.Ключ));
			Если ОбъектМетаданных <> Неопределено Тогда
				ИсточникиЧерезЗапятую = ОбъектМетаданных.ПолноеИмя();
			КонецЕсли;
		КонецЕсли;
		Если ИсточникиЧерезЗапятую = "" Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В формах отчетов, обработок и в общих формах
					|при вызове процедуры ""%1""
					|требуется явно указывать параметр ""%2""'"),
				"ПодключаемыеКоманды.ПриСозданииНаСервере",
				"ПараметрыРазмещения.Источники");
		КонецЕсли;
	КонецЕсли;
	
	КэшФормы = ПодключаемыеКомандыПовтИсп.КэшФормы(ИмяФормы, ИсточникиЧерезЗапятую, ЭтоФормаОбъекта);
	ПараметрыРазмещения.Вставить("ЕстьУсловияВидимости", КэшФормы.ЕстьУсловияВидимости);
	ПараметрыРазмещения.Вставить("ЭтоФормаОбъекта", КэшФормы.ЭтоФормаОбъекта);
	ПараметрыРазмещения.Вставить("ВводНаОснованииЧерезПодключаемыеКоманды", КэшФормы.ВводНаОснованииЧерезПодключаемыеКоманды);
	
	Если КэшФормы.ФункциональныеОпции.Количество() > 0 Тогда
		Форма.УстановитьПараметрыФункциональныхОпцийФормы(КэшФормы.ФункциональныеОпции);
	КонецЕсли;
	
	Команды = КэшФормы.Команды.Скопировать();
	ВывестиКоманды(Форма, Команды, ПараметрыРазмещения);
	
КонецПроцедуры

// Конструктор одноименного параметра процедуры ПодключаемыеКоманды.ПриСозданииНаСервере.
//
// Возвращаемое значение:
//   Структура - параметры размещения подключаемых команд:
//       * Источники - ОписаниеТипов
//                   - Массив из ОбъектМетаданных - источники команд.
//           Используется для второстепенных списков, а также в формах объектов, не являющихся поставщиками
//           команд (обработки, общие формы).
//       * КоманднаяПанель - ГруппаФормы - командная панель или группа команд, в которой выводятся подменю.
//           Используется как родитель для создания подменю в случае их отсутствия.
//           Если не указан то в первую очередь ищется группа "ПодключаемыеКоманды".
//       * ПрефиксГрупп - Строка - добавка к именам подменю и имени командной панели.
//           Используется при необходимости префиксации групп с командами (в частности, когда в форме несколько таблиц).
//           В качестве префикса рекомендуется использовать имя таблицы формы, для которой выводятся команды.
//           Например, если ПрефиксГрупп = "СкладскиеДокументы" (имя второстепенной таблицы формы),
//           то используются подменю с именами "СкладскиеДокументыПодменюПечать", "СкладскиеДокументыПодменюОтчеты" и т.д.
//       * ВладелецКоманд - ДанныеФормыСтруктура, ТаблицаФормы - объект или элемент формы, для которого выводятся команды.
//
Функция ПараметрыРазмещения() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("Источники");
	Результат.Вставить("КоманднаяПанель");
	Результат.Вставить("ПрефиксГрупп", "");
	Результат.Вставить("ВладелецКоманд");
	
	Возврат Результат;
	
КонецФункции

// Обработчик команды формы, требующей контекстного вызова сервера.
//
// Параметры:
//   Форма - ФормаКлиентскогоПриложения - форма, из которой выполняется команда.
//   ПараметрыВызова - Структура
//   Источник - ТаблицаФормы
//            - ДанныеФормыСтруктура - объект или список формы с полем "Ссылка".
//   Результат - Структура - результат выполнения команды.
//
Процедура ВыполнитьКоманду(Знач Форма, Знач ПараметрыВызова, Знач Источник = Неопределено, Результат = Неопределено) Экспорт
	
	Если ТипЗнч(ПараметрыВызова) <> Тип("Структура")
		Или ПараметрыВызова.Количество() <> 3
		Или ТипЗнч(Форма) <> Тип("ФормаКлиентскогоПриложения") Тогда
		Возврат;
	КонецЕсли;
	
	Если Источник = Неопределено Тогда
		Источник = ПодключаемыеКомандыКлиентСервер.ВладелецКомандыПоИмениКоманды(ПараметрыВызова.ИмяКомандыВФорме, Форма);
	КонецЕсли;
	
	АдресНастроек = Форма.ПараметрыПодключаемыхКоманд.АдресТаблицыКоманд;
	ОписаниеКоманды = ОписаниеКоманды(ПараметрыВызова.ИмяКомандыВФорме, АдресНастроек);
	
	ПараметрыВыполнения = ПараметрыВыполненияКоманды();
	ПараметрыВыполнения.ОписаниеКоманды = Новый Структура(ОписаниеКоманды);
	ПараметрыВыполнения.Форма = Форма;
	ПараметрыВыполнения.ЭтоФормаОбъекта = ТипЗнч(Источник) = Тип("ДанныеФормыСтруктура");
	ПараметрыВыполнения.Источник = Источник;
	
	ПараметрыЭкспортнойПроцедуры = Новый Массив;
	ПараметрыЭкспортнойПроцедуры.Добавить(ПараметрыВызова.ПараметрКоманды);
	ПараметрыЭкспортнойПроцедуры.Добавить(ПараметрыВыполнения);
	
	Обработчик = ОписаниеКоманды.Обработчик;
	ПрефиксОбщегоМодуля = НРег("ОбщийМодуль.");
	Если СтрНачинаетсяС(НРег(Обработчик), ПрефиксОбщегоМодуля) Тогда
		Обработчик = Сред(Обработчик, СтрДлина(ПрефиксОбщегоМодуля) + 1);
	КонецЕсли;
	
	ОбщегоНазначения.ВыполнитьМетодКонфигурации(Обработчик, ПараметрыЭкспортнойПроцедуры);
	
	Результат = ПараметрыВыполнения.Результат;
	ПараметрыВызова.Результат = ПараметрыВыполнения.Результат;
	
КонецПроцедуры

// Задает условия видимости команды на форме в зависимости от контекста.
//
// Параметры:
//   Команда      - СтрокаТаблицыЗначений из см. УправлениеПечатью.СоздатьКоллекциюКомандПечати
//   Реквизит     - Строка                - имя реквизита объекта.
//   Значение     - Произвольный          - значение реквизита объекта. Параметр обязательный для всех видов
//                                          сравнения кроме Заполнено и НеЗаполнено.
//   ВидСравнения - ВидСравненияКомпоновкиДанных - вид сравнения значений.
//       Допустимо использовать следующие виды сравнения:
//         ВидСравненияКомпоновкиДанных.Равно,
//         ВидСравненияКомпоновкиДанных.НеРавно,
//         ВидСравненияКомпоновкиДанных.Заполнено,
//         ВидСравненияКомпоновкиДанных.НеЗаполнено,
//         ВидСравненияКомпоновкиДанных.ВСписке,
//         ВидСравненияКомпоновкиДанных.НеВСписке,
//         ВидСравненияКомпоновкиДанных.Больше,
//         ВидСравненияКомпоновкиДанных.Меньше,
//         ВидСравненияКомпоновкиДанных.БольшеИлиРавно,
//         ВидСравненияКомпоновкиДанных.МеньшеИлиРавно.
//       Значение по умолчанию - ВидСравненияКомпоновкиДанных.Равно.
//
Процедура ДобавитьУсловиеВидимостиКоманды(Команда, Реквизит, Значение = Неопределено, Знач ВидСравнения = Неопределено) Экспорт
	Если ВидСравнения = Неопределено Тогда
		ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
	КонецЕсли;
	УсловиеВидимости = Новый Структура;
	УсловиеВидимости.Вставить("Реквизит", Реквизит);
	УсловиеВидимости.Вставить("ВидСравнения", ВидСравнения);
	УсловиеВидимости.Вставить("Значение", Значение);
	Команда.УсловияВидимости.Добавить(УсловиеВидимости);
КонецПроцедуры

// Свойства второго параметра обработчика подключаемой команды, выполняемой на сервере.
//
// Возвращаемое значение:
//  Структура:
//   * ОписаниеКоманды - Структура - состав свойств совпадает с колонками таблицы значений параметра Команды процедуры
///                                  ПодключаемыеКомандыПереопределяемый.ПриОпределенииКомандПодключенныхКОбъекту.
//                                   Ключевые свойства:
//      ** Идентификатор  - Строка - идентификатор команды.
//      ** Представление  - Строка - представление команды в форме.
//      ** Имя            - Строка - имя команды в форме.
//      ** ДополнительныеПараметры - Структура - дополнительные свойства, состав которых определяется видом 
//                                   конкретной команды.
//   * Форма - ФормаКлиентскогоПриложения - форма, из которой вызвана команда.
//   * ЭтоФормаОбъекта - Булево - Истина, если команда вызвана из формы объекта.
//   * Источник - ТаблицаФормы
//              - ДанныеФормыСтруктура - объект или список формы с полем "Ссылка".
//
Функция ПараметрыВыполненияКоманды() Экспорт
	ПараметрыВыполнения = ПодключаемыеКомандыКлиентСервер.ПараметрыВыполненияКоманды();
	// Служебные параметры.
	Результат = Новый Структура;
	Результат.Вставить("Текст",    "");
	Результат.Вставить("Подробно", "");
	ПараметрыВыполнения.Вставить("Результат", Результат);
	Возврат ПараметрыВыполнения;
КонецФункции

#КонецОбласти

#Область СлужебныйПрограммныйИнтерфейс

////////////////////////////////////////////////////////////////////////////////
// Обработчики событий.

// Формирует таблицу общих настроек всех расширений, подключенных к объекту метаданных.
Функция ПодключенныеОбъекты(ОписаниеИсточника, ПодключенныеОбъекты = Неопределено, НастройкиПрограммногоИнтерфейса = Неопределено) Экспорт
	Источники = ДеревоИсточниковКоманд();
	Если ТипЗнч(ОписаниеИсточника) = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
		Источник = Источники.Строки.Добавить();
		Источник.СсылкаМетаданных = ОписаниеИсточника;
		Источник.ТипСсылкиДанных = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ОписаниеИсточника, "ЗначениеПустойСсылки");
	Иначе
		Источник = ОписаниеИсточника;
	КонецЕсли;
	
	Если ПодключенныеОбъекты = Неопределено Тогда
		ПодключенныеОбъекты = ТаблицаПодключаемыхОбъектов(НастройкиПрограммногоИнтерфейса);
	КонецЕсли;
	Если Источник.СсылкаМетаданных = Неопределено Тогда
		Возврат ПодключенныеОбъекты;
	КонецЕсли;
	
	ПолныеИменаПодключенныхОбъектов = ПодключаемыеКомандыПовтИсп.Параметры().ПодключенныеОбъекты[Источник.СсылкаМетаданных];
	Если ПолныеИменаПодключенныхОбъектов = Неопределено Тогда
		Возврат ПодключенныеОбъекты;
	КонецЕсли;
	
	Для Каждого ПолноеИмя Из ПолныеИменаПодключенныхОбъектов Цикл
		ПодключенныйОбъект = ПодключенныеОбъекты.Найти(ПолноеИмя, "ПолноеИмя");
		Если ПодключенныйОбъект = Неопределено Тогда
			НастройкиПодключаемогоОбъекта = НастройкиПодключаемогоОбъекта(ПолноеИмя, НастройкиПрограммногоИнтерфейса);
			Если НастройкиПодключаемогоОбъекта = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ПодключенныйОбъект = ПодключенныеОбъекты.Добавить();
			ЗаполнитьЗначенияСвойств(ПодключенныйОбъект, НастройкиПодключаемогоОбъекта);
			ПодключенныйОбъект.ТипСсылкиДанных = Источник.ТипСсылкиДанных;
			ПодключенныйОбъект.Метаданные = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя);
		Иначе
			ПодключенныйОбъект.ТипСсылкиДанных = ОбъединитьТипы(ПодключенныйОбъект.ТипСсылкиДанных, Источник.ТипСсылкиДанных);
		КонецЕсли;
	КонецЦикла;
	
	Возврат ПодключенныеОбъекты;
КонецФункции

// Получает настройки интеграции объекта метаданных - поставщика команд (отчета или обработки).
//
// Параметры:
//   ПолноеИмя - Строка - полное имя объекта метаданных.
//   НастройкиПрограммногоИнтерфейса - см. ПодключаемыеКоманды.НастройкиПрограммногоИнтерфейсаПодключаемыхОбъектов.
//
// Возвращаемое значение:
//  Структура - настройки интеграции этого объекта:
//   * Размещение - Массив из ОбъектМетаданных - объекты, к которым подключен объект.
//   * ДобавитьКомандыПечати     - Булево - в модуле менеджера объекта определена функция ДобавитьКомандыПечати. 
//   * ДобавитьКомандыЗаполнения - Булево - в модуле менеджера объекта определена функция ДобавитьКомандыЗаполнения. 
//   * ДобавитьКомандыОтчетов    - Булево - в модуле менеджера объекта определена функция ДобавитьКомандыОтчетов. 
//   * НастроитьВариантыОтчета   - Булево - в модуле менеджера объекта определена функция НастроитьВариантыОтчета. 
//   * ОпределитьНастройкиФормы  - Булево - в модуле менеджера объекта определена функция ОпределитьНастройкиФормы. 
//   * Вид - Строка - вид имя объекта метаданных в верхнем регистре.
//   * ПолноеИмя - Строка - полное имя объекта метаданных.
//   * Менеджер - ОбработкаМенеджер
//              - ОтчетМенеджер - менеджер объекта метаданных.
//  Неопределено - если настройки не удалось получить.
//
Функция НастройкиПодключаемогоОбъекта(ПолноеИмя, НастройкиПрограммногоИнтерфейса = Неопределено) Экспорт
	ЧастиИмени = СтрРазделить(ПолноеИмя, ".");
	Если ЧастиИмени.Количество() <> 2 Тогда
		Возврат Неопределено;
	КонецЕсли;
	ВидВРег = ВРег(ЧастиИмени[0]);
	Имя = ЧастиИмени[1];
	Если ВидВРег = "ОТЧЕТ" Тогда
		Узел = Отчеты;
	ИначеЕсли ВидВРег = "ОБРАБОТКА" Тогда
		Узел = Обработки;
	Иначе
		Возврат Неопределено;
	КонецЕсли;
	
	Если НастройкиПрограммногоИнтерфейса = Неопределено Тогда
		НастройкиПрограммногоИнтерфейса = НастройкиПрограммногоИнтерфейсаПодключаемыхОбъектов();
	КонецЕсли;
	
	Настройки = Новый Структура;
	Для Каждого Настройка Из НастройкиПрограммногоИнтерфейса Цикл
		Если Настройка.ВидыПодключаемыхОбъектов = ""
			Или СтрНайти(ВРег(Настройка.ВидыПодключаемыхОбъектов), ВидВРег) > 0 Тогда
			Настройки.Вставить(Настройка.Ключ, Настройка.ОписаниеТипов.ПривестиЗначение());
		КонецЕсли;
	КонецЦикла;
	
	Менеджер = Узел[Имя];
	Попытка
		Менеджер.ПриОпределенииНастроек(Настройки);
	Исключение
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось прочитать настройки объекта %1 из модуля менеджера:'"), ПолноеИмя);
		ТекстОшибки = ТекстОшибки + Символы.ПС + ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Подключаемые команды'", ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка,
			ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя),
			ПолноеИмя,
			ТекстОшибки);
		Возврат Неопределено;
	КонецПопытки;
	
	Настройки.Вставить("Вид",       ВидВРег);
	Настройки.Вставить("ПолноеИмя", ПолноеИмя);
	Настройки.Вставить("Менеджер",  Менеджер);
	Возврат Настройки;
КонецФункции

// Добавляет типы в массив.
//
// Параметры:
//   Массив - Массив - массив типов.
//   ТипИлиОписаниеТипов - Тип
//                       - ОписаниеТипов - добавляемые типы.
//
Процедура ДополнитьМассивТипов(Массив, ТипИлиОписаниеТипов) Экспорт
	Если ТипЗнч(ТипИлиОписаниеТипов) = Тип("ОписаниеТипов") Тогда
		ОбщегоНазначенияКлиентСервер.ДополнитьМассив(Массив, ТипИлиОписаниеТипов.Типы(), Истина);
	ИначеЕсли ТипЗнч(ТипИлиОписаниеТипов) = Тип("Тип") И Массив.Найти(ТипИлиОписаниеТипов) = Неопределено Тогда
		Массив.Добавить(ТипИлиОписаниеТипов);
	КонецЕсли;
КонецПроцедуры

// Регистрирует в дереве источников команд объект метаданных, а также вспомогательные объекты метаданных,
//   подключенных к указанному объекту метаданных.
//
// Параметры:
//   ОбъектМетаданных - ОбъектМетаданных - объект метаданных, к которому подключены источники команд.
//   Источники - см. ПодключаемыеКомандыПереопределяемый.ПриОпределенииКомандПодключенныхКОбъекту.Источники.
//   ПодключенныеОбъекты - см. ПодключаемыеКоманды.ТаблицаПодключаемыхОбъектов
//   НастройкиПрограммногоИнтерфейса - см. ПодключаемыеКоманды.НастройкиПрограммногоИнтерфейсаПодключаемыхОбъектов
//
// Возвращаемое значение:
//   СтрокаДереваЗначений - настройки объекта метаданных. См. описание 2 параметра
//       процедуры ПодключаемыеКомандыПереопределяемый.ПриОпределенииКомандПодключенныхКОбъекту().
//
Функция ЗарегистрироватьИсточник(ОбъектМетаданных, Источники, ПодключенныеОбъекты, НастройкиПрограммногоИнтерфейса) Экспорт
	Если ОбъектМетаданных = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
	Менеджер  = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	Если Менеджер = Неопределено Тогда
		Возврат Неопределено; // Объект не может быть источником команд.
	КонецЕсли;
	
	Источник = Источники.Строки.Добавить();
	Источник.Метаданные          = ОбъектМетаданных;
	Источник.ПолноеИмя           = ПолноеИмя;
	Источник.Менеджер            = Менеджер;
	Источник.СсылкаМетаданных    = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ПолноеИмя);
	Источник.Вид                 = ВРег(СтрРазделить(ПолноеИмя, ".")[0]);
	Источник.ЭтоЖурналДокументов = (Источник.Вид = "ЖУРНАЛДОКУМЕНТОВ");
	
	Если Источник.ЭтоЖурналДокументов Тогда
		МассивТипов = Новый Массив;
		Для Каждого МетаданныеДокумента Из ОбъектМетаданных.РегистрируемыеДокументы Цикл
			Документ = ЗарегистрироватьИсточник(МетаданныеДокумента, Источник, ПодключенныеОбъекты, НастройкиПрограммногоИнтерфейса);
			Если Документ <> Неопределено Тогда
				МассивТипов.Добавить(Документ.ТипСсылкиДанных);
			КонецЕсли;
		КонецЦикла;
		Источник.ТипСсылкиДанных = Новый ОписаниеТипов(МассивТипов);
	ИначеЕсли Не Метаданные.Обработки.Содержит(ОбъектМетаданных) И Не Метаданные.Отчеты.Содержит(ОбъектМетаданных) Тогда
		Источник.ТипСсылкиДанных = Тип(Источник.Вид + "Ссылка." + ОбъектМетаданных.Имя);
	КонецЕсли;
	
	ПодключенныеОбъекты(Источник, ПодключенныеОбъекты, НастройкиПрограммногоИнтерфейса);
	
	Возврат Источник;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Шаблоны.

// Шаблон сведений об объектах метаданных - источников команд.
//
// Возвращаемое значение:
//   см. ПодключаемыеКомандыПереопределяемый.ПриОпределенииКомандПодключенныхКОбъекту.Источники.
//
Функция ДеревоИсточниковКоманд() Экспорт
	Результат = Новый ДеревоЗначений;
	Результат.Колонки.Добавить("Метаданные");
	Результат.Колонки.Добавить("ПолноеИмя", Новый ОписаниеТипов("Строка"));
	Результат.Колонки.Добавить("Менеджер");
	Результат.Колонки.Добавить("СсылкаМетаданных");
	Результат.Колонки.Добавить("ТипСсылкиДанных");
	Результат.Колонки.Добавить("Вид", Новый ОписаниеТипов("Строка"));
	Результат.Колонки.Добавить("ЭтоЖурналДокументов", Новый ОписаниеТипов("Булево"));
	Возврат Результат;
КонецФункции

// Шаблон сведений об отчетах и обработках, подключенных к источникам команд.
//
// Возвращаемое значение:
//   ТаблицаЗначений - вспомогательные параметры:
//       * ПолноеИмя  - Строка           - полное имя объекта. Например: "Документ.ИмяДокумента".
//       * Менеджер   - Произвольный     - модуль менеджера объекта.
//       * Размещение - Массив           - список объектов, к которым подключен отчет или обработка.
//       * ТипСсылкиДанных - Тип
//                         - ОписаниеТипов - тип объектов, к которым подключен отчет или обработка..
//
Функция ТаблицаПодключаемыхОбъектов(НастройкиПрограммногоИнтерфейса = Неопределено) Экспорт
	Если НастройкиПрограммногоИнтерфейса = Неопределено Тогда
		НастройкиПрограммногоИнтерфейса = НастройкиПрограммногоИнтерфейсаПодключаемыхОбъектов();
	КонецЕсли;
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("ПолноеИмя", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("Менеджер");
	Таблица.Колонки.Добавить("Метаданные");
	Таблица.Колонки.Добавить("ТипСсылкиДанных");
	
	Для Каждого Настройка Из НастройкиПрограммногоИнтерфейса Цикл
		Попытка
			Таблица.Колонки.Добавить(Настройка.Ключ, Настройка.ОписаниеТипов);
		Исключение
			ТекстОшибки = НСтр("ru = 'Не удалось зарегистрировать настройку программного интерфейса подключаемых объектов.
				|Ключ: ""%1"", описание типов: ""%2"", описание ошибки: ""%3"".'");
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				ТекстОшибки,
				Настройка.Ключ,
				Строка(Настройка.ОписаниеТипов),
				ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
			ВызватьИсключение ТекстОшибки;
		КонецПопытки;
	КонецЦикла;
	
	Таблица.Индексы.Добавить("ПолноеИмя");
	
	Возврат Таблица;
КонецФункции

// Шаблон сведений об отчетах и обработках, подключенных к источникам команд.
//
// Возвращаемое значение:
//   ТаблицаЗначений - описание параметра Настройки процедуры ПриОпределенииНастроек объектов,
//       включенных в состав подсистемы ПодключаемыеОтчетыИОбработки:
//       * Ключ             - Строка        - имя настройки.
//       * ОписаниеТипов    - ОписаниеТипов - тип настройки.
//       * ВидыПодключаемыхОбъектов - Строка - вид объекта метаданных в верхнем регистре.
//                                            Например: "ОТЧЕТ" или "ОБРАБОТКА".
//
Функция НастройкиПрограммногоИнтерфейсаПодключаемыхОбъектов() Экспорт
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Ключ", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("ОписаниеТипов", Новый ОписаниеТипов("ОписаниеТипов"));
	Таблица.Колонки.Добавить("ВидыПодключаемыхОбъектов", Новый ОписаниеТипов("Строка"));
	
	Настройка = Таблица.Добавить();
	Настройка.Ключ          = "Размещение";
	Настройка.ОписаниеТипов = Новый ОписаниеТипов("Массив");
	Настройка.ВидыПодключаемыхОбъектов = "Отчет, Обработка";
	
	ЗаполнениеОбъектов.ПриОпределенииСоставаНастроекПодключаемыхОбъектов(Таблица);
	СозданиеНаОсновании.ПриОпределенииСоставаНастроекПодключаемыхОбъектов(Таблица);
	ИнтеграцияПодсистемБСП.ПриОпределенииСоставаНастроекПодключаемыхОбъектов(Таблица);
	ПодключаемыеКомандыПереопределяемый.ПриОпределенииСоставаНастроекПодключаемыхОбъектов(Таблица);
	
	Возврат Таблица;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Обработчики событий подсистем конфигурации.

// См. ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковОбновления.
Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт
	Обработчик = Обработчики.Добавить();
	Обработчик.ВыполнятьВГруппеОбязательных = Ложь;
	Обработчик.ОбщиеДанные                  = Истина;
	Обработчик.УправлениеОбработчиками      = Ложь;
	Обработчик.РежимВыполнения              = "Оперативно";
	Обработчик.Версия    = "*";
	Обработчик.Процедура = "ПодключаемыеКоманды.ОперативноеОбновлениеОбщихДанныхКонфигурации";
	Обработчик.Приоритет = 90;
КонецПроцедуры

// Обработчик обновления кэшей, связанных с расширениями.
Функция ПриЗаполненииВсехПараметровРаботыРасширений() Экспорт
	Возврат ОперативноеОбновлениеОбщихДанных(Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений"));
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Обновление информационной базы.

Функция ОперативноеОбновлениеОбщихДанныхКонфигурации() Экспорт
	Возврат ОперативноеОбновлениеОбщихДанных(Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных"));
КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

////////////////////////////////////////////////////////////////////////////////
// Кэш формы ПриСозданииНаСервере.

// Кэш формы, в которой будут выводиться подключаемые команды.
Функция КэшФормы(ИмяФормы, ИсточникиЧерезЗапятую, ЭтоФормаОбъекта) Экспорт
	Команды = ТаблицаКоманд();
	Источники = ДеревоИсточниковКоманд();
	НастройкиПрограммногоИнтерфейса = НастройкиПрограммногоИнтерфейсаПодключаемыхОбъектов();
	ПодключенныеОбъекты = ТаблицаПодключаемыхОбъектов(НастройкиПрограммногоИнтерфейса);
	
	КэшФормы = Новый Структура;
	КэшФормы.Вставить("Команды", Команды);
	КэшФормы.Вставить("ЕстьУсловияВидимости", Ложь);
	КэшФормы.Вставить("ФункциональныеОпции", Новый Структура);
	
	МетаданныеФормы = Метаданные.НайтиПоПолномуИмени(ИмяФормы);
	МетаданныеРодителя = ?(МетаданныеФормы = Неопределено, Неопределено, МетаданныеФормы.Родитель());
	ВидВРег = ВРег(СтрРазделить(ИмяФормы, ".")[0]);
	ТипыИсточников = Новый Массив;
	Если ИсточникиЧерезЗапятую = "" Тогда
		Источник = ЗарегистрироватьИсточник(МетаданныеРодителя, Источники, ПодключенныеОбъекты, НастройкиПрограммногоИнтерфейса);
		ДополнитьМассивТипов(ТипыИсточников, Источник.ТипСсылкиДанных);
	Иначе
		ПолныеИменаИсточников = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ИсточникиЧерезЗапятую, ",", Истина, Истина);
		Для Каждого ПолноеИмя Из ПолныеИменаИсточников Цикл
			ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя);
			Источник = ЗарегистрироватьИсточник(ОбъектМетаданных, Источники, ПодключенныеОбъекты, НастройкиПрограммногоИнтерфейса);
			ДополнитьМассивТипов(ТипыИсточников, Источник.ТипСсылкиДанных);
		КонецЦикла;
	КонецЕсли;
	
	Если ЭтоФормаОбъекта = Истина И ТипыИсточников.Количество() = 1 И МетаданныеРодителя <> Метаданные.НайтиПоТипу(ТипыИсточников[0]) Тогда
		ЭтоФормаОбъекта = Ложь; // В форме объекта размещен список от другого типа объекта.
	КонецЕсли;
	
	Если ЭтоФормаОбъекта = Неопределено Тогда
		Если ТипыИсточников.Количество() > 1 Тогда
			ЭтоФормаОбъекта = Ложь;
		ИначеЕсли МетаданныеРодителя <> Неопределено Тогда
			Коллекция = Новый Структура("ОсновнаяФормаСписка, ОсновнаяФормаОбъекта");
			ЗаполнитьЗначенияСвойств(Коллекция, МетаданныеРодителя);
			Если МетаданныеФормы = Коллекция.ОсновнаяФормаСписка Тогда
				ЭтоФормаОбъекта = Ложь;
			ИначеЕсли МетаданныеФормы = Коллекция.ОсновнаяФормаОбъекта И МетаданныеРодителя <> Метаданные.НайтиПоТипу(ТипыИсточников[0]) Тогда
				ЭтоФормаОбъекта = Истина;
			Иначе
				Если ВидВРег = ВРег("ЖурналДокументов") Тогда
					ЭтоФормаОбъекта = Ложь;
				ИначеЕсли ВидВРег = ВРег("Обработка") Тогда
					ЭтоФормаОбъекта = Ложь;
				Иначе
					ЭтоФормаОбъекта = Истина;
				КонецЕсли;
			КонецЕсли;
		Иначе
			ЭтоФормаОбъекта = Ложь;
		КонецЕсли;
	КонецЕсли;
	КэшФормы.Вставить("ЭтоФормаОбъекта", ЭтоФормаОбъекта);
	
	Контекст = Новый Структура;
	Контекст.Вставить("ВидВРег", ВидВРег);
	Контекст.Вставить("ИмяФормы", ИмяФормы);
	Контекст.Вставить("МетаданныеФормы", МетаданныеФормы);
	Контекст.Вставить("ТипыИсточников", ТипыИсточников);
	Контекст.Вставить("ЭтоФормаОбъекта", ЭтоФормаОбъекта);
	Контекст.Вставить("ФункциональныеОпции", КэшФормы.ФункциональныеОпции);
	
	КэшФормы.Вставить("ВводНаОснованииЧерезПодключаемыеКоманды", СозданиеНаОсновании.ОбъектыПодключеныКПодсистеме(ТипыИсточников));
	Если КэшФормы.ВводНаОснованииЧерезПодключаемыеКоманды Тогда
		СозданиеНаОсновании.ПриОпределенииКомандПодключенныхКОбъекту(Контекст, Источники, ПодключенныеОбъекты, Команды);
	КонецЕсли;
	
	ЗаполнениеОбъектов.ПриОпределенииКомандПодключенныхКОбъекту(Контекст, Источники, ПодключенныеОбъекты, Команды);
	ИнтеграцияПодсистемБСП.ПриОпределенииКомандПодключенныхКОбъекту(Контекст, Источники, ПодключенныеОбъекты, Команды);
	ПодключаемыеКомандыПереопределяемый.ПриОпределенииКомандПодключенныхКОбъекту(Контекст, Источники, ПодключенныеОбъекты, Команды);
	
	// Фильтр команд по именам форм и функциональным опциям.
	ЧастиИмени = СтрРазделить(ИмяФормы, ".");
	КраткоеИмяФормы = ЧастиИмени[ЧастиИмени.ВГраница()];
	Количество = Команды.Количество();
	Для Номер = 1 По Количество Цикл
		Команда = Команды[Количество - Номер];
		// Значения по умолчанию.
		Если Команда.ИзменяетВыбранныеОбъекты = Неопределено Тогда
			Команда.ИзменяетВыбранныеОбъекты = Ложь;
		КонецЕсли;
		
		// Фильтр по назначению.
		Если Команда.Назначение = "ДляСписка" И Контекст.ЭтоФормаОбъекта Или Команда.Назначение = "ДляОбъекта" И Не Контекст.ЭтоФормаОбъекта Тогда
			Команды.Удалить(Команда);
			Продолжить;
		КонецЕсли;
		
		// Фильтр по именам форм.
		ВидимостьВФормах = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ВРег(Команда.ВидимостьВФормах), ",", Истина, Истина);
		Если ВидимостьВФормах.Количество() > 0
			И ВидимостьВФормах.Найти(ВРег(КраткоеИмяФормы)) = Неопределено
			И ВидимостьВФормах.Найти(ВРег(ИмяФормы)) = Неопределено Тогда
			Команды.Удалить(Команда);
			Продолжить;
		КонецЕсли;
		// Фильтр по функциональным опциям.
		ФункциональныеОпции = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(Команда.ФункциональныеОпции, ",", Истина, Истина);
		ВидимостьКоманды = ФункциональныеОпции.Количество() = 0;
		Для Каждого ИмяОпции Из ФункциональныеОпции Цикл
			Если ПолучитьФункциональнуюОпцию(СокрЛП(ИмяОпции)) Тогда
				ВидимостьКоманды = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Не ВидимостьКоманды Тогда
			Команды.Удалить(Команда);
			Продолжить;
		КонецЕсли;
		// Динамические применяемые условия видимости.
		Если ТипЗнч(Команда.ТипПараметра) = Тип("Тип") Тогда
			МассивТипов = Новый Массив;
			МассивТипов.Добавить(Команда.ТипПараметра);
			Команда.ТипПараметра = Новый ОписаниеТипов(МассивТипов);
		КонецЕсли;
		Если ТипЗнч(Команда.ТипПараметра) = Тип("ОписаниеТипов") И ЗначениеЗаполнено(Команда.ТипПараметра) Тогда
			СодержитХотьОдинТип = Ложь;
			Для Каждого Тип Из ТипыИсточников Цикл
				Если Команда.ТипПараметра.СодержитТип(Тип) Тогда
					СодержитХотьОдинТип = Истина;
				Иначе
					Команда.ЕстьУсловияВидимости = Истина;
				КонецЕсли;
			КонецЦикла;
			Если Не СодержитХотьОдинТип Тогда
				Команды.Удалить(Команда);
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		Если ТипЗнч(Команда.УсловияВидимости) = Тип("Массив") И Команда.УсловияВидимости.Количество() > 0 Тогда
			Команда.ЕстьУсловияВидимости = Истина;
		КонецЕсли;
		Если Команда.МножественныйВыбор = Неопределено Тогда
			Команда.МножественныйВыбор = Истина;
		КонецЕсли;
		Команда.ПорядокВажности = ?(Команда.Важность = "Важное", 1, ?(Команда.Важность = "СмТакже", 3, 2));
		КэшФормы.ЕстьУсловияВидимости = КэшФормы.ЕстьУсловияВидимости Или Команда.ЕстьУсловияВидимости;
		
		Если ПустаяСтрока(Команда.Идентификатор) Тогда
			Команда.Идентификатор = "Авто_" + ОбщегоНазначения.КонтрольнаяСуммаСтрокой(Команда.Менеджер + "/" + Команда.ИмяФормы + "/" + Команда.Обработчик);
		КонецЕсли;
	КонецЦикла;
	
	Возврат КэшФормы;
КонецФункции

Процедура ПроверитьИмяВидаКоманд(ИмяВида) Экспорт
	
	Если Не ОбщегоНазначенияКлиентСервер.ИмяСоответствуетТребованиямИменованияСвойств(ИмяВида) Тогда
		ТекстОшибки = НСтр("ru = 'Имя вида команд ""%1"" не удовлетворяет требованиям именования переменных.'");
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстОшибки, ИмяВида);
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Вывод.

// Размещает подключенные команды в форме.
//
// Параметры:
//   Форма - ФормаКлиентскогоПриложения - форма, в которой необходимо разместить команды.
//   Команды - см. ТаблицаКоманд
//   ПараметрыРазмещения - см. ПараметрыРазмещения
//
Процедура ВывестиКоманды(Форма, Команды, ПараметрыРазмещения)
	
	ПрефиксКоманд = "";
	Если ТипЗнч(ПараметрыРазмещения.ВладелецКоманд) = Тип("ТаблицаФормы") Тогда
		ПрефиксКоманд = "Элемент_" + ПараметрыРазмещения.ВладелецКоманд.Имя;
	ИначеЕсли ТипЗнч(ПараметрыРазмещения.ВладелецКоманд) = Тип("ДанныеФормыСтруктура") Тогда
		Для Каждого Реквизит Из Форма.ПолучитьРеквизиты() Цикл
			Если Реквизит = ПараметрыРазмещения.ВладелецКоманд Тогда
				ПрефиксКоманд = "Реквизит_" + Реквизит.Имя;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ПараметрыРазмещения.ВладелецКоманд = ПрефиксКоманд;
	
	ПодключенныеКоманды = ПодключенныеКоманды(Форма);
	ПодключенныеКоманды.ВводНаОснованииЧерезПодключаемыеКоманды = ПараметрыРазмещения.ВводНаОснованииЧерезПодключаемыеКоманды;
	
	КлючПараметровРазмещения = КлючПараметровРазмещения(ПараметрыРазмещения);
	РанееДобавленныеКоманды = Неопределено;
	
	Если ПодключенныеКоманды.АдресТаблицыКоманд <> Неопределено И ЭтоАдресВременногоХранилища(ПодключенныеКоманды.АдресТаблицыКоманд) Тогда
		РанееДобавленныеКоманды = ПолучитьИзВременногоХранилища(ПодключенныеКоманды.АдресТаблицыКоманд);
		УдаляемыеКоманды = РанееДобавленныеКоманды.НайтиСтроки(Новый Структура("КлючПараметровРазмещения", КлючПараметровРазмещения));
		УдалитьКоманды(Форма, УдаляемыеКоманды);
		Для Каждого Команда Из УдаляемыеКоманды Цикл
			КоллекцияКоманд = ПодключенныеКоманды.КомандыСПометкой;
			Для Индекс = -КоллекцияКоманд.ВГраница() По 0 Цикл
				КомандаПодменю = КоллекцияКоманд[-Индекс];
				Если КомандаПодменю.ИмяВФорме = Команда.ИмяВФорме Тогда
					КоллекцияКоманд.Удалить(-Индекс);
				КонецЕсли;
			КонецЦикла;
			Для Каждого Подменю Из ПодключенныеКоманды.ПодменюСУсловиямиВидимости Цикл
				КоллекцияКоманд = Подменю.КомандыСУсловиямиВидимости;
				Для Индекс = -КоллекцияКоманд.ВГраница() По 0 Цикл
					КомандаПодменю = КоллекцияКоманд[-Индекс];
					Если КомандаПодменю.ИмяВФорме = Команда.ИмяВФорме Тогда
						КоллекцияКоманд.Удалить(-Индекс);
					КонецЕсли;
				КонецЦикла;
			КонецЦикла;
			РанееДобавленныеКоманды.Удалить(Команда);
		КонецЦикла;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ПрефиксКоманд) И ПодключенныеКоманды.ВладельцыКоманд.Найти(ПрефиксКоманд) = Неопределено Тогда
		ПодключенныеКоманды.ВладельцыКоманд.Добавить(ПрефиксКоманд);
	КонецЕсли;
	
	ПодключенныеКоманды.ЕстьУсловияВидимости = ПодключенныеКоманды.ЕстьУсловияВидимости Или ПараметрыРазмещения.ЕстьУсловияВидимости;
	
	Элементы = Форма.Элементы;
	ПрефиксГрупп = ?(ЗначениеЗаполнено(ПараметрыРазмещения.ПрефиксГрупп), ПараметрыРазмещения.ПрефиксГрупп, "");
	
	КоманднаяПанель = ПараметрыРазмещения.КоманднаяПанель;
	Если КоманднаяПанель = Неопределено Тогда
		КоманднаяПанель = КоманднаяПанельФормы(Форма, ПрефиксГрупп, ПараметрыРазмещения.ЭтоФормаОбъекта);
	КонецЕсли;
	
	СведенияОВсехПодменю = СведенияОВсехПодменю();
	БыстрыйПоискСведенийОПодменю = Новый Соответствие;
	
	КорневыеПодменюИКоманды = ПодключенныеКоманды.КорневыеПодменюИКоманды;
	
	// Вывод команд.
	Команды.Сортировать("Вид, ПорядокВажности Возр, Порядок Возр, Представление Возр");
	СчетчикКомандСАвтогенерируемымИменем = 0;
	ВидыКоманд = ПодключаемыеКомандыПовтИсп.ВидыКоманд();
	
	Для Каждого ВидКоманд Из ВидыКоманд Цикл
		КомандыВида = Команды.НайтиСтроки(Новый Структура("Вид", ВидКоманд.Имя)); // Массив из СтрокаТаблицыЗначений: см. ТаблицаКоманд
		
		Если КомандыВида.Количество() = 0 И ВидКоманд.Имя <> "СозданиеНаОсновании" Тогда
			Продолжить;
		КонецЕсли;
		
		ИмяПодменюПоУмолчанию = "";
		Если Не ПустаяСтрока(ВидКоманд.ИмяПодменю) Тогда
			ИмяПодменюПоУмолчанию = ПрефиксГрупп + ВидКоманд.ИмяПодменю;
		КонецЕсли;
		
		СведенияОПодменюПоУмолчанию = БыстрыйПоискСведенийОПодменю.Получить(НРег(ИмяПодменюПоУмолчанию));
		Если СведенияОПодменюПоУмолчанию = Неопределено Тогда
			СведенияОПодменюПоУмолчанию = ЗарегистрироватьПодменю(Элементы, СведенияОВсехПодменю, ИмяПодменюПоУмолчанию, 
				ВидКоманд, КоманднаяПанель);
			БыстрыйПоискСведенийОПодменю.Вставить(НРег(ИмяПодменюПоУмолчанию), СведенияОПодменюПоУмолчанию);
		КонецЕсли;
		
		Для Каждого Команда Из КомандыВида Цикл 
			Если ПустаяСтрока(Команда.Подменю) Тогда
				СведенияОПодменюКоманды = СведенияОПодменюПоУмолчанию;
			Иначе
				ИмяПодменю = ПрефиксГрупп + Команда.Подменю;
				СведенияОПодменюКоманды = БыстрыйПоискСведенийОПодменю.Получить(НРег(ИмяПодменю));
				Если СведенияОПодменюКоманды = Неопределено Тогда
					СведенияОПодменюКоманды = ЗарегистрироватьПодменю(Элементы, СведенияОВсехПодменю, ИмяПодменю,,,
						СведенияОПодменюПоУмолчанию);
					БыстрыйПоискСведенийОПодменю.Вставить(НРег(ИмяПодменю), СведенияОПодменюКоманды);
				КонецЕсли;
			КонецЕсли;
			
			ГруппаФормы = Неопределено; // ГруппаФормы
			Если Не ЗначениеЗаполнено(Команда.Важность)
				Или Не СведенияОПодменюКоманды.Группы.Свойство(Команда.Важность, ГруппаФормы) Тогда
				ГруппаФормы = СведенияОПодменюКоманды.ГруппаПоУмолчанию;
			КонецЕсли;
			
			Команда.ИмяВФорме = ОпределитьИмяКоманды(Форма, ГруппаФормы.Имя, Команда.Идентификатор, 
				СчетчикКомандСАвтогенерируемымИменем, ПрефиксКоманд);
			Команда.КлючПараметровРазмещения = КлючПараметровРазмещения;
			
			Подменю = СведенияОПодменюКоманды.Подменю; // ГруппаФормы
			ИмяКорневогоЭлемента = ?(ВидКоманд.Имя = "КоманднаяПанель", Команда.ИмяВФорме, Подменю.Имя);
					
			КомандаФормы = Форма.Команды.Добавить(Команда.ИмяВФорме);
			КомандаФормы.Действие = "Подключаемый_ВыполнитьКоманду";
			КомандаФормы.Заголовок = Команда.Представление;
			КомандаФормы.Подсказка   = КомандаФормы.Заголовок;
			КомандаФормы.Отображение = ?(ЗначениеЗаполнено(Команда.ОтображениеКнопки),
				Команда.ОтображениеКнопки, ОтображениеКнопки.КартинкаИТекст);
			Если ТипЗнч(Команда.Картинка) = Тип("Картинка") Тогда
				КомандаФормы.Картинка = Команда.Картинка;
			КонецЕсли;
			Если ТипЗнч(Команда.СочетаниеКлавиш) = Тип("СочетаниеКлавиш") Тогда
				КомандаФормы.СочетаниеКлавиш = Команда.СочетаниеКлавиш;
			КонецЕсли;
			Если СведенияОПодменюКоманды.Подменю = КоманднаяПанель
				И СтрДлина(Команда.Представление) > 35
				И ЗначениеЗаполнено(КомандаФормы.Картинка) Тогда
				КомандаФормы.Отображение = ОтображениеКнопки.Картинка;
			КонецЕсли;
			КомандаФормы.ИзменяетСохраняемыеДанные = Команда.ИзменяетВыбранныеОбъекты 
				И ПараметрыРазмещения.ЭтоФормаОбъекта И Форма.ТолькоПросмотр;
			
			КнопкаФормы = Элементы.Добавить(Команда.ИмяВФорме, Тип("КнопкаФормы"), ГруппаФормы);
			КнопкаФормы.Вид = ВидКнопкиФормы.КнопкаКоманднойПанели;
			КнопкаФормы.ИмяКоманды = Команда.ИмяВФорме;
			Если Команда.ТолькоВоВсехДействиях = Истина Тогда
				ПоложениеВКоманднойПанели = ПоложениеКнопкиВКоманднойПанели.ВДополнительномПодменю;
			ИначеЕсли Команда.ТолькоВоВсехДействиях = Ложь Тогда
				ПоложениеВКоманднойПанели = ПоложениеКнопкиВКоманднойПанели.ВКоманднойПанелиИВДополнительномПодменю;
			Иначе
				ПоложениеВКоманднойПанели = ПоложениеКнопкиВКоманднойПанели.Авто;
			КонецЕсли;
			КнопкаФормы.ПоложениеВКоманднойПанели = ПоложениеВКоманднойПанели;
			
			Если ЗначениеЗаполнено(Команда.ЗначениеПометки) И ЭтоПутьДоРеквизита(Команда.ЗначениеПометки) Тогда
				Команда.ЗначениеПометки = ВыражениеВычислениеПометки(Команда.ЗначениеПометки);
				ПодключенныеКоманды.КомандыСПометкой.Добавить(ОписаниеКомандыНаКлиенте(Команда, КнопкаФормы, ПрефиксКоманд));
			КонецЕсли;
			
			СвойстваКорневогоПодменюКоманды = КорневыеПодменюИКоманды[ИмяКорневогоЭлемента];
			Если СвойстваКорневогоПодменюКоманды = Неопределено Тогда
				СвойстваКорневогоПодменюКоманды = СвойстваКорневогоПодменюКоманды();
				СвойстваКорневогоПодменюКоманды.ЕстьВКоманднойПанели = 
					КнопкаФормы.ПоложениеВКоманднойПанели <> ПоложениеКнопкиВКоманднойПанели.ВДополнительномПодменю;
				СвойстваКорневогоПодменюКоманды.ПрефиксКоманд = ПрефиксКоманд;
				КорневыеПодменюИКоманды.Вставить(ИмяКорневогоЭлемента, СвойстваКорневогоПодменюКоманды);
			КонецЕсли;
			
			СведенияОПодменюКоманды.ВыведеноКоманд = СведенияОПодменюКоманды.ВыведеноКоманд + 1;
			СведенияОПодменюКоманды.ПоследняяКоманда = КомандаФормы;
			Если Команда.ЕстьУсловияВидимости Тогда
				СведенияОПодменюКоманды.ЕстьКомандыСУсловиямиВидимости = Истина;
				
				СведенияОКоманде = Новый Структура;
				СведенияОКоманде.Вставить("ИмяВФорме");
				СведенияОКоманде.Вставить("ТипПараметра");
				СведенияОКоманде.Вставить("УсловияВидимости");
				СведенияОКоманде.Вставить("УсловияВидимостиПоТипамОбъектов");
				СведенияОКоманде.Вставить("ПрефиксКоманд");
				ЗаполнитьЗначенияСвойств(СведенияОКоманде, Команда);
				
				СведенияОКоманде.ПрефиксКоманд = ПрефиксКоманд;
				СведенияОПодменюКоманды.КомандыСУсловиямиВидимости.Добавить(СведенияОКоманде);
			ИначеЕсли Не Команда.ТолькоВоВсехДействиях Тогда
				СведенияОПодменюКоманды.ЕстьКомандыБезУсловийВидимости = Истина;
			КонецЕсли;
		КонецЦикла;
		
		СозданиеНаОсновании.ПриВыводеКоманд(Форма, ВидКоманд, СведенияОПодменюПоУмолчанию, ПараметрыРазмещения);
	КонецЦикла;
	
	// Команда-заглушка нужна всегда.
	КомандаЗаглушка = Форма.Команды.Найти("КомандаДляВыводаВПустоеПодменю");
	Если КомандаЗаглушка = Неопределено Тогда
		КомандаЗаглушка = Форма.Команды.Добавить("КомандаДляВыводаВПустоеПодменю");
		КомандаЗаглушка.Заголовок = НСтр("ru = '(нет)'");
	КонецЕсли;
	
	// Постобработка задействованных подменю.
	Для Каждого СведенияОПодменю Из СведенияОВсехПодменю Цикл
		Если СведенияОПодменю.ВыведеноКоманд = 0 Тогда
			Продолжить;
		КонецЕсли;
		ЭтоКоманднаяПанель = (СведенияОПодменю.Подменю = КоманднаяПанель);
		КомандаФормы = СведенияОПодменю.ПоследняяКоманда;
		Подменю = СведенияОПодменю.Подменю; // ГруппаФормы
		
		Если Не ЭтоКоманднаяПанель Тогда
			Если СведенияОПодменю.ВыведеноКоманд = 1 И КомандаФормы <> Неопределено Тогда
				// Превращение подменю в кнопку когда выведена 1 команда с коротким заголовком.
				Если Не ЗначениеЗаполнено(КомандаФормы.Картинка) И Подменю.Вид = ВидГруппыФормы.Подменю Тогда
					КомандаФормы.Картинка = Подменю.Картинка;
				КонецЕсли;
				
				Если Не ЗначениеЗаполнено(КомандаФормы.Картинка) Тогда
					КомандаФормы.Картинка = СведенияОПодменю.КартинкаПодменю;
				КонецЕсли;			
				
				Если СтрДлина(КомандаФормы.Заголовок) <= 35 И Подменю.Отображение <> ОтображениеКнопки.Картинка Тогда
					КомандаФормы.Отображение = ОтображениеКнопки.КартинкаИТекст;
				Иначе
					КомандаФормы.Отображение = ОтображениеКнопки.Картинка;
				КонецЕсли;
				Подменю.Вид = ВидГруппыФормы.ГруппаКнопок;
				КомандаФормы.Подсказка = КомандаФормы.Заголовок;
			Иначе
				// Добавление кнопок-заглушек, которые показываются когда в подменю скрыты все команды.
				ИмяКомандыЗаглушки = Подменю.Имя + "Заглушка";
				Если Элементы.Найти(ИмяКомандыЗаглушки) = Неопределено Тогда
					КнопкаФормы = Элементы.Добавить(ИмяКомандыЗаглушки, Тип("КнопкаФормы"), Подменю);
					КнопкаФормы.Вид = ВидКнопкиФормы.КнопкаКоманднойПанели;
					КнопкаФормы.ИмяКоманды  = "КомандаДляВыводаВПустоеПодменю";
					КнопкаФормы.Видимость   = Ложь;
					КнопкаФормы.Доступность = Ложь;
					КнопкаФормы.ПоложениеВКоманднойПанели = ПоложениеКнопкиВКоманднойПанели.ВКоманднойПанели;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		
		Если СведенияОПодменю.ЕстьКомандыСУсловиямиВидимости Тогда
			КраткиеСведенияОПодменю = КраткиеСведенияОПодменю(СведенияОПодменю);
			КраткиеСведенияОПодменю.Имя = Подменю.Имя;
			ПодключенныеКоманды.ПодменюСУсловиямиВидимости.Добавить(КраткиеСведенияОПодменю);
		КонецЕсли;
	КонецЦикла;
	
	Если РанееДобавленныеКоманды <> Неопределено Тогда
		РанееДобавленныеКоманды = ПолучитьИзВременногоХранилища(ПодключенныеКоманды.АдресТаблицыКоманд);
		Если ТипЗнч(РанееДобавленныеКоманды) = Тип("ТаблицаЗначений") Тогда
			Индекс = -1;
			Для Каждого СтрокаТаблицы Из РанееДобавленныеКоманды Цикл
				Индекс = Индекс + 1;
				ЗаполнитьЗначенияСвойств(Команды.Вставить(Индекс), СтрокаТаблицы);
			КонецЦикла;
		КонецЕсли;
		УдалитьИзВременногоХранилища(ПодключенныеКоманды.АдресТаблицыКоманд);
	КонецЕсли;
	
	Команды.Колонки.Удалить("СочетаниеКлавиш");
	ПодключенныеКоманды.АдресТаблицыКоманд = ПоместитьВоВременноеХранилище(Команды, Форма.УникальныйИдентификатор);
	
КонецПроцедуры

// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Подменю - ГруппаФормы 
//   * ВыведеноКоманд - Число
//   * ЕстьКомандыСУсловиямиВидимости - Булево
//   * ЕстьКомандыБезУсловийВидимости - Булево
//   * Группы - Структура:
//    ** Обычное - ГруппаФормы
//    ** Важное - ГруппаФормы
//    ** СмТакже - ГруппаФормы
//   * ГруппаПоУмолчанию - ГруппаФормы
//   * ПоследняяКоманда - КомандаФормы
//   * КомандыСУсловиямиВидимости - Массив
//   * КартинкаПодменю - Картинка
//
Функция СведенияОВсехПодменю()
	
	СведенияОВсехПодменю = Новый ТаблицаЗначений;
	СведенияОВсехПодменю.Колонки.Добавить("Подменю");
	СведенияОВсехПодменю.Колонки.Добавить("ВыведеноКоманд", Новый ОписаниеТипов("Число"));
	СведенияОВсехПодменю.Колонки.Добавить("ЕстьКомандыСУсловиямиВидимости", Новый ОписаниеТипов("Булево"));
	СведенияОВсехПодменю.Колонки.Добавить("ЕстьКомандыБезУсловийВидимости", Новый ОписаниеТипов("Булево"));
	СведенияОВсехПодменю.Колонки.Добавить("Группы", Новый ОписаниеТипов("Структура"));
	СведенияОВсехПодменю.Колонки.Добавить("ГруппаПоУмолчанию");
	СведенияОВсехПодменю.Колонки.Добавить("ПоследняяКоманда");
	СведенияОВсехПодменю.Колонки.Добавить("КомандыСУсловиямиВидимости", Новый ОписаниеТипов("Массив"));
	СведенияОВсехПодменю.Колонки.Добавить("КартинкаПодменю");
	
	Возврат СведенияОВсехПодменю;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//   * Имя - Строка
//   * КомандыСУсловиямиВидимости - Массив
//   * ЕстьКомандыБезУсловийВидимости - Булево
//
Функция КраткиеСведенияОПодменю(СведенияОПодменю)
	КраткиеСведенияОПодменю = Новый Структура("Имя, КомандыСУсловиямиВидимости, ЕстьКомандыБезУсловийВидимости");
	ЗаполнитьЗначенияСвойств(КраткиеСведенияОПодменю, СведенияОПодменю);
	Возврат КраткиеСведенияОПодменю;
КонецФункции

Функция КлючПараметровРазмещения(Знач ПараметрыРазмещения)
	
	ПараметрыРазмещения = ОбщегоНазначения.СкопироватьРекурсивно(ПараметрыРазмещения);
	ГруппаФормы = ПараметрыРазмещения.КоманднаяПанель;
	Если ТипЗнч(ГруппаФормы) = Тип("ГруппаФормы") Тогда
		ПараметрыРазмещения.КоманднаяПанель = ГруппаФормы.Имя;
	КонецЕсли;
	Источники = Новый Массив;
	Если ТипЗнч(ПараметрыРазмещения.Источники) = Тип("Массив") Тогда
		Для Каждого ОбъектМетаданных Из ПараметрыРазмещения.Источники Цикл
			Источники.Добавить(ОбъектМетаданных.ПолноеИмя());
		КонецЦикла;
		ПараметрыРазмещения.Источники = Источники;
	КонецЕсли;
	
	Возврат ОбщегоНазначения.КонтрольнаяСуммаСтрокой(ОбщегоНазначения.ЗначениеВСтрокуXML(ПараметрыРазмещения));

КонецФункции

Процедура УдалитьКоманды(Форма, УдаляемыеКоманды)
	
	Для Каждого ОписаниеКоманды Из УдаляемыеКоманды Цикл
		Команда = Форма.Команды[ОписаниеКоманды.ИмяВФорме];
		Форма.Команды.Удалить(Команда);
		Форма.Элементы.Удалить(Форма.Элементы[ОписаниеКоманды.ИмяВФорме]);
	КонецЦикла;
	
КонецПроцедуры

Функция КоманднаяПанельФормы(Форма, ПрефиксГрупп, ЭтоФормаОбъекта)
	
	Элементы = Форма.Элементы;
	
	Результат = Элементы.Найти(ПрефиксГрупп + "ПодключаемыеКоманды");
	Если Результат = Неопределено Тогда
		Результат = Элементы.Найти(ПрефиксГрупп + "КоманднаяПанель");
		
		Если Результат = Неопределено Тогда
			Результат = Элементы.Найти(ПрефиксГрупп + "ОсновнаяКоманднаяПанель");
			
			Если Результат = Неопределено И ЗначениеЗаполнено(ПрефиксГрупп) Тогда
				ТаблицаФормы = Элементы.Найти(ПрефиксГрупп);
				Если ТипЗнч(ТаблицаФормы) = Тип("ТаблицаФормы") Тогда
					Результат = ТаблицаФормы.КоманднаяПанель;
				КонецЕсли;
			КонецЕсли;
			
			Если Не ЭтоФормаОбъекта
				И Результат = Неопределено
				И Не ЗначениеЗаполнено(ПрефиксГрупп) Тогда
				ТаблицаФормы = Элементы.Найти("Список");
				Если ТипЗнч(ТаблицаФормы) = Тип("ТаблицаФормы")
					И ТаблицаФормы.ПоложениеКоманднойПанели <> ПоложениеКоманднойПанелиЭлементаФормы.Нет Тогда
					Результат = ТаблицаФормы.КоманднаяПанель;
				КонецЕсли;
			КонецЕсли;
			
			Если Результат = Неопределено Тогда
				Результат = Форма.КоманднаяПанель;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;

КонецФункции

Функция ПодключенныеКоманды(Форма)
	
	ЗначенияСвойств = Новый Структура("ПараметрыПодключаемыхКоманд", Null);
	ЗаполнитьЗначенияСвойств(ЗначенияСвойств, Форма);

	Результат = ЗначенияСвойств.ПараметрыПодключаемыхКоманд;
	
	Если ТипЗнч(Результат) <> Тип("Структура") Тогда
		Если Результат = Null Тогда
			ДобавляемыеРеквизиты = Новый Массив;
			ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы("ПараметрыПодключаемыхКоманд", Новый ОписаниеТипов));
			Форма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
		КонецЕсли;
		
		Результат = Новый Структура;
		Результат.Вставить("ЕстьУсловияВидимости", Ложь);
		Результат.Вставить("ПодменюСУсловиямиВидимости", Новый Массив);
		Результат.Вставить("КомандыСПометкой", Новый Массив);
		Результат.Вставить("КорневыеПодменюИКоманды", Новый Соответствие);
		Результат.Вставить("ДоступностьКоманд", Истина);
		Результат.Вставить("АдресТаблицыКоманд", Неопределено);
		Результат.Вставить("ВводНаОснованииЧерезПодключаемыеКоманды");
		Результат.Вставить("ВладельцыКоманд", Новый Массив);
		
		Форма.ПараметрыПодключаемыхКоманд = Результат;
	КонецЕсли;
	
	Возврат Результат;

КонецФункции

Функция СвойстваКорневогоПодменюКоманды()
	
	Результат = Новый Структура;
	Результат.Вставить("ЕстьВКоманднойПанели", Ложь);
	Результат.Вставить("ПрефиксКоманд", "");
	Результат.Вставить("ДоступностьКоманд", Истина);
	
	Возврат Результат;
	
КонецФункции

Функция ЭтоПутьДоРеквизита(Знач ПутьДоРеквизита)
	ПутьДоРеквизита = ВРег(ПутьДоРеквизита);
	ПутьДоРеквизита = СтрЗаменить(ПутьДоРеквизита, "НЕ ", "");
	Идентификаторы = СтрРазделить(СтрЗаменить(ПутьДоРеквизита, "%ИСТОЧНИК%",""), ".");
	Для Каждого Элемент Из Идентификаторы Цикл
		Если Не ОбщегоНазначенияКлиентСервер.ИмяСоответствуетТребованиямИменованияСвойств(Элемент) Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Истина;
КонецФункции

Функция ОписаниеКомандыНаКлиенте(Команда, КнопкаФормы, ПрефиксКоманд)
	ОписаниеКоманды = Новый Структура;
	ОписаниеКоманды.Вставить("ИмяВФорме", КнопкаФормы.Имя);
	ОписаниеКоманды.Вставить("Вид", Команда.Вид);
	ОписаниеКоманды.Вставить("Идентификатор", Команда.Идентификатор);
	ОписаниеКоманды.Вставить("ЗначениеПометки", Команда.ЗначениеПометки);
	ОписаниеКоманды.Вставить("ПрефиксКоманд", ПрефиксКоманд);
	Возврат ОписаниеКоманды;
КонецФункции

Функция ЗарегистрироватьПодменю(Элементы, СведенияОВсехПодменю, ИмяПодменю, ШаблонНового = Неопределено, 
	КоманднаяПанель = Неопределено, ПодменюПоУмолчанию = Неопределено)
	
	ВыведеноКоманд = 0;
	Группы = Новый Структура;
	КартинкаПодменю = Неопределено;
	Если ЗначениеЗаполнено(ИмяПодменю) Тогда
		Подменю = Элементы.Найти(ИмяПодменю);
		Если Подменю = Неопределено Тогда
			Если ШаблонНового = Неопределено Тогда
				Возврат ПодменюПоУмолчанию;
			КонецЕсли;
			Подменю = Элементы.Добавить(ИмяПодменю, Тип("ГруппаФормы"), КоманднаяПанель);
			Подменю.Вид         = ?(ЗначениеЗаполнено(ШаблонНового.ВидГруппыФормы), ШаблонНового.ВидГруппыФормы, 
				ВидГруппыФормы.Подменю);
			Если ЗначениеЗаполнено(ШаблонНового.Отображение) Тогда
				Подменю.Отображение = ШаблонНового.Отображение;
			КонецЕсли;
			Подменю.Заголовок   = ШаблонНового.Заголовок;
			Если Подменю.Вид = ВидГруппыФормы.Подменю Тогда
				Подменю.Картинка    = ШаблонНового.Картинка;
				КартинкаПодменю		= Подменю.Картинка;
			КонецЕсли;
		Иначе
			ГруппаПоУмолчанию = Подменю;
			Если Подменю.Вид = ВидГруппыФормы.Подменю Тогда
				КартинкаПодменю		= Подменю.Картинка;
			ИначеЕсли ШаблонНового <> Неопределено Тогда
				КартинкаПодменю 	= ШаблонНового.Картинка;
			ИначеЕсли ПодменюПоУмолчанию <> Неопределено Тогда
				КартинкаПодменю 	= ПодменюПоУмолчанию.КартинкаПодменю;
			КонецЕсли;
			ВыведеноКоманд = КоличествоКомандВГруппе(ГруппаПоУмолчанию);
			Для Каждого Группа Из Подменю.ПодчиненныеЭлементы Цикл
				Если ТипЗнч(Группа) <> Тип("ГруппаФормы") Тогда
					Продолжить;
				КонецЕсли;
				КраткоеИмя = Группа.Имя;
				Если СтрНачинаетсяС(НРег(КраткоеИмя), НРег(ИмяПодменю)) Тогда
					КраткоеИмя = Сред(КраткоеИмя, СтрДлина(ИмяПодменю) + 1);
					Если НРег(КраткоеИмя) = НРег("Обычное") Тогда
						ГруппаПоУмолчанию = Группа;
					КонецЕсли;
				КонецЕсли;
				Группы.Вставить(КраткоеИмя, Группа);
			КонецЦикла;
		КонецЕсли;
		
		Если Подменю.Отображение = ОтображениеКнопки.Картинка И Не ЗначениеЗаполнено(Подменю.Подсказка) Тогда
			Подменю.Подсказка = Подменю.Заголовок;
		КонецЕсли;
		
		Если Не Группы.Свойство("Важное") Тогда
			ГруппаВажное = Элементы.Добавить(ИмяПодменю + "Важное", Тип("ГруппаФормы"), Подменю);
			ГруппаВажное.Вид = ВидГруппыФормы.ГруппаКнопок;
			ГруппаВажное.Заголовок = Подменю.Заголовок + " (" + НСтр("ru = 'Важное'") + ")";
			Группы.Вставить("Важное", ГруппаВажное);
		КонецЕсли;
		Если Не Группы.Свойство("Обычное") Тогда
			ГруппаПоУмолчанию = Элементы.Добавить(ИмяПодменю + "Обычное", Тип("ГруппаФормы"), Подменю);
			ГруппаПоУмолчанию.Вид = ВидГруппыФормы.ГруппаКнопок;
			ГруппаПоУмолчанию.Заголовок = Подменю.Заголовок + " (" + НСтр("ru = 'Обычное'") + ")";
			Группы.Вставить("Обычное", ГруппаПоУмолчанию);
		КонецЕсли;
		Если Не Группы.Свойство("СмТакже") Тогда
			ГруппаСмТакже = Элементы.Добавить(ИмяПодменю + "СмТакже", Тип("ГруппаФормы"), Подменю);
			ГруппаСмТакже.Вид = ВидГруппыФормы.ГруппаКнопок;
			ГруппаСмТакже.Заголовок = Подменю.Заголовок + " (" + НСтр("ru = 'См. также'") + ")";
			Группы.Вставить("СмТакже", ГруппаСмТакже);
		КонецЕсли;
		
	Иначе
		Если ШаблонНового = Неопределено Тогда
			Возврат ПодменюПоУмолчанию;
		КонецЕсли;
		Подменю = КоманднаяПанель;
		ГруппаПоУмолчанию = КоманднаяПанель;
	КонецЕсли;
	
	Результат = СведенияОВсехПодменю.Добавить();
	Результат.Подменю = Подменю;
	Результат.ГруппаПоУмолчанию = ГруппаПоУмолчанию;
	Результат.Группы = Группы;
	Результат.ВыведеноКоманд = ВыведеноКоманд;
	Результат.КартинкаПодменю = КартинкаПодменю;
	
	Возврат Результат;
КонецФункции

Функция КоличествоКомандВГруппе(Группа)
	Результат = 0;
	Для Каждого Элемент Из Группа.ПодчиненныеЭлементы Цикл
		Если ТипЗнч(Элемент) = Тип("ГруппаФормы") Тогда
			Результат = Результат + КоличествоКомандВГруппе(Элемент);
		ИначеЕсли ТипЗнч(Элемент) = Тип("КнопкаФормы") Тогда
			Результат = Результат + 1;
		КонецЕсли;
	КонецЦикла;
	Возврат Результат;
КонецФункции

Функция ОпределитьИмяКоманды(Форма, ИмяГруппы, ИдентификаторКоманды, СчетчикКомандСАвтогенерируемымИменем, ПрефиксКоманд)
	Если ОбщегоНазначенияКлиентСервер.ИмяСоответствуетТребованиямИменованияСвойств(ИдентификаторКоманды) Тогда
		ИмяКоманды = ИмяГруппы + "_" + ПрефиксКоманд + "_" +  ИдентификаторКоманды;
	Иначе
		СчетчикКомандСАвтогенерируемымИменем = СчетчикКомандСАвтогенерируемымИменем + 1;
		ИмяКоманды = ИмяГруппы + "_" + ПрефиксКоманд + "_" + Формат(СчетчикКомандСАвтогенерируемымИменем, "ЧН=; ЧГ=");
	КонецЕсли;
	Пока Форма.Элементы.Найти(ИмяКоманды) <> Неопределено
		Или Форма.Команды.Найти(ИмяКоманды) <> Неопределено Цикл
		СчетчикКомандСАвтогенерируемымИменем = СчетчикКомандСАвтогенерируемымИменем + 1;
		ИмяКоманды = ИмяГруппы + "_" + ПрефиксКоманд + "_" + Формат(СчетчикКомандСАвтогенерируемымИменем, "ЧН=; ЧГ=");
	КонецЦикла;
	Возврат ИмяКоманды;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Обновление информационной базы.

// Обновляет кэш объектов метаданных указанного типа.
//
// Параметры:
//  Отбор - СправочникСсылка.ИдентификаторыОбъектовМетаданных - обновлять кэш конфигурации.
//                      Структура с ключом "ПодключенныеОбъекты" записывается в константу ПараметрыПодключаемыхКоманд.
//        - СправочникСсылка.ИдентификаторыОбъектовРасширений - обновлять кэш расширений.
//                      Структура с ключом "ПодключенныеОбъекты" записывается в регистр ПараметрыРаботыВерсийРасширений.
//
// Возвращаемое значение:
//   Структура:
//       * ЕстьИзменения - Булево - Истина когда обновление прошло не "в холостую".
//       * ПодключенныеОбъекты - Соответствие из КлючИЗначение - кэш для быстрого определения списка объектов,
//           подключенных к объектам конфигурации:
//           ** Ключ - СправочникСсылка.ИдентификаторыОбъектовМетаданных, СправочникСсылка.ИдентификаторыОбъектовРасширений
//           ** Значение - Массив из Строка
//
Функция ОперативноеОбновлениеОбщихДанных(Отбор)
	Результат = Новый Структура;
	Результат.Вставить("ЕстьИзменения", Ложь);
	
	Если Отбор = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений")
		И Не ЗначениеЗаполнено(ПараметрыСеанса.ВерсияРасширений) Тогда
		Возврат Результат;
	КонецЕсли;
	
	ПодключенныеОбъекты = Новый Соответствие;
	НастройкиПрограммногоИнтерфейса = НастройкиПрограммногоИнтерфейсаПодключаемыхОбъектов();
	
	Состав = Метаданные.Подсистемы.ПодключаемыеОтчетыИОбработки.Состав;
	Для Каждого ОбъектМетаданныхПоставщика Из Состав Цикл
		Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() И ОбъектМетаданныхПоставщика.РасширениеКонфигурации() <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		ИдентификаторОбъектаМетаданных = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданныхПоставщика, Ложь);
		Если ТипЗнч(ИдентификаторОбъектаМетаданных) <> Отбор Тогда
			Продолжить;
		КонецЕсли;
		ПолноеИмя = ОбъектМетаданныхПоставщика.ПолноеИмя();
		Настройки = НастройкиПодключаемогоОбъекта(ПолноеИмя, НастройкиПрограммногоИнтерфейса);
		Если Настройки = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Для Каждого ОбъектМетаданных Из Настройки.Размещение Цикл
			ИдентификаторОбъектаМетаданных = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных);
			МассивПриемник = ПодключенныеОбъекты[ИдентификаторОбъектаМетаданных];
			Если МассивПриемник = Неопределено Тогда
				МассивПриемник = Новый Массив;
				ПодключенныеОбъекты.Вставить(ИдентификаторОбъектаМетаданных, МассивПриемник);
			КонецЕсли;
			Если МассивПриемник.Найти(ПолноеИмя) = Неопределено Тогда
				МассивПриемник.Добавить(ПолноеИмя);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Если Отбор = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
		СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыПрограммы(ПолноеИмяПодсистемы());
	ИначеЕсли Отбор = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений") Тогда
		СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ПолноеИмяПодсистемы());
	Иначе
		Возврат Результат;
	КонецЕсли;
	
	НовоеЗначение = Новый Структура("ПодключенныеОбъекты", ПодключенныеОбъекты);
	Если Не ОбщегоНазначения.ДанныеСовпадают(СтароеЗначение, НовоеЗначение) Тогда
		Результат.ЕстьИзменения = Истина;
		Если Отбор = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
			СтандартныеПодсистемыСервер.УстановитьПараметрРаботыПрограммы(ПолноеИмяПодсистемы(), НовоеЗначение);
		ИначеЕсли Отбор = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений") Тогда
			СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ПолноеИмяПодсистемы(), НовоеЗначение);
		КонецЕсли;
	КонецЕсли;
	
	Результат.Вставить("ПодключенныеОбъекты", ПодключенныеОбъекты);
	Возврат Результат;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Вызовы из модулей ВызовСервера.

// Возвращает описание команды по имени элемента формы.
// 
// Параметры:
//  ИмяКомандыВФорме Имя команды в форме
//  АдресНастроек Адрес настроек
// 
// Возвращаемое значение:
//  ФиксированнаяСтруктура:
//   * Вид - Строка
//   * Идентификатор - Строка
//   * Представление - Строка
//   * Подменю - Строка
//   * Важность - Строка
//   * Порядок - Число
//   * Картинка - Картинка
//   * СочетаниеКлавиш - СочетаниеКлавиш
//   * ОтображениеКнопки - Неопределено
//   * ТолькоВоВсехДействиях - Булево
//   * ЗначениеПометки - Строка
//   * ТипПараметра - ОписаниеТипов
//   * ВидимостьВФормах - Строка
//   * Назначение - Строка
//   * ФункциональныеОпции - Строка
//   * УсловияВидимости - Массив
//   * ИзменяетВыбранныеОбъекты - Булево
//   * МножественныйВыбор - Булево, Неопределено
//   * РежимЗаписи - Строка
//   * ТребуетсяРаботаСФайлами - Булево
//   * Менеджер - Строка
//   * Обработчик - Строка
//   * ДополнительныеПараметры - Структура
//   * ИмяФормы - Строка
//   * ПараметрыФормы - Структура, Неопределено
//   * ИмяПараметраФормы - Строка
//   * ПорядокВажности - Число
//   * ИмяВФорме - Строка
//   * ЕстьУсловияВидимости - Булево
//   * КлючПараметровРазмещения - Строка
//
Функция ОписаниеКоманды(ИмяКомандыВФорме, АдресНастроек) Экспорт
	Команды = ПолучитьИзВременногоХранилища(АдресНастроек);
	Команда = Команды.Найти(ИмяКомандыВФорме, "ИмяВФорме");
	Если Команда = Неопределено Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Сведения о команде ""%1"" не существуют.'"),
			ИмяКомандыВФорме);
	КонецЕсли;
	ОписаниеКоманды = ОбщегоНазначения.СтрокаТаблицыЗначенийВСтруктуру(Команда);
	
	Если ЗначениеЗаполнено(ОписаниеКоманды.ИмяФормы) Тогда
		ОписаниеКоманды.Вставить("Серверная", Ложь);
		МассивПодстрок = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ОписаниеКоманды.ИмяФормы, ".", Истина, Истина);
		КоличествоПодстрок = МассивПодстрок.Количество();
		Если КоличествоПодстрок = 1
			Или (КоличествоПодстрок = 2 И ВРег(МассивПодстрок[0]) <> "ОБЩАЯФОРМА") Тогда
			ОписаниеКоманды.ИмяФормы = ОписаниеКоманды.Менеджер + "." + ОписаниеКоманды.ИмяФормы;
		КонецЕсли;
	Иначе
		ОписаниеКоманды.Вставить("Серверная", Истина);
		Если ЗначениеЗаполнено(ОписаниеКоманды.Обработчик) Тогда
			Если Не ПустаяСтрока(ОписаниеКоманды.Менеджер) И СтрНайти(ОписаниеКоманды.Обработчик, ".") = 0 Тогда
				ОписаниеКоманды.Обработчик = ОписаниеКоманды.Менеджер + "." + ОписаниеКоманды.Обработчик;
			КонецЕсли;
			Если СтрНачинаетсяС(ВРег(ОписаниеКоманды.Обработчик), ВРег("ОбщийМодуль.")) Тогда
				ПозицияТочки = СтрНайти(ОписаниеКоманды.Обработчик, ".");
				ОписаниеКоманды.Обработчик = Сред(ОписаниеКоманды.Обработчик, ПозицияТочки + 1);
			КонецЕсли;
			МассивПодстрок = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ОписаниеКоманды.Обработчик, ".", Истина, Истина);
			КоличествоПодстрок = МассивПодстрок.Количество();
			Если КоличествоПодстрок = 2 Тогда
				ИмяМодуля = МассивПодстрок[0];
				ОбъектМетаданныхОбщийМодуль = Метаданные.ОбщиеМодули.Найти(ИмяМодуля);
				Если ОбъектМетаданныхОбщийМодуль = Неопределено Тогда
					ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Общий модуль ""%1"" не существует.'"),
						ИмяМодуля);
				КонецЕсли;
				Если ОбъектМетаданныхОбщийМодуль.КлиентУправляемоеПриложение Тогда
					ОписаниеКоманды.Серверная = Ложь;
				КонецЕсли;
			Иначе
				Вид = ВРег(МассивПодстрок[0]);
				ВидВоМножественномЧисле = ВидОбъектаМетаданныхВоМножественномЧисле(Вид);
				Если ВидВоМножественномЧисле <> Неопределено Тогда
					МассивПодстрок.Установить(0, ВидВоМножественномЧисле);
					ОписаниеКоманды.Обработчик = СтрСоединить(МассивПодстрок, ".");
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	ОписаниеКоманды.Удалить("Менеджер");
	
	Возврат Новый ФиксированнаяСтруктура(ОписаниеКоманды);
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Работа с объектами метаданных.

// Возвращает вид объекта во множественном числе.
Функция ВидОбъектаМетаданныхВоМножественномЧисле(Знач Вид)
	Вид = ВРег(СокрЛП(Вид));
	Если Вид = "ПЛАНОБМЕНА" Тогда
		Возврат "ПланыОбмена";
	ИначеЕсли Вид = "СПРАВОЧНИК" Тогда
		Возврат "Справочники";
	ИначеЕсли Вид = "ДОКУМЕНТ" Тогда
		Возврат "Документы";
	ИначеЕсли Вид = "ЖУРНАЛДОКУМЕНТОВ" Тогда
		Возврат "ЖурналыДокументов";
	ИначеЕсли Вид = "ПЕРЕЧИСЛЕНИЕ" Тогда
		Возврат "Перечисления";
	ИначеЕсли Вид = "ОТЧЕТ" Тогда
		Возврат "Отчеты";
	ИначеЕсли Вид = "ОБРАБОТКА" Тогда
		Возврат "Обработки";
	ИначеЕсли Вид = "ПЛАНВИДОВХАРАКТЕРИСТИК" Тогда
		Возврат "ПланыВидовХарактеристик";
	ИначеЕсли Вид = "ПЛАНСЧЕТОВ" Тогда
		Возврат "ПланыСчетов";
	ИначеЕсли Вид = "ПЛАНВИДОВРАСЧЕТА" Тогда
		Возврат "ПланыВидовРасчета";
	ИначеЕсли Вид = "РЕГИСТРСВЕДЕНИЙ" Тогда
		Возврат "РегистрыСведений";
	ИначеЕсли Вид = "РЕГИСТРНАКОПЛЕНИЯ" Тогда
		Возврат "РегистрыНакопления";
	ИначеЕсли Вид = "РЕГИСТРБУХГАЛТЕРИИ" Тогда
		Возврат "РегистрыБухгалтерии";
	ИначеЕсли Вид = "РЕГИСТРРАСЧЕТА" Тогда
		Возврат "РегистрыРасчета";
	ИначеЕсли Вид = "ПЕРЕРАСЧЕТ" Тогда
		Возврат "Перерасчеты";
	ИначеЕсли Вид = "БИЗНЕСПРОЦЕСС" Тогда
		Возврат "БизнесПроцессы";
	ИначеЕсли Вид = "ЗАДАЧА" Тогда
		Возврат "Задачи";
	ИначеЕсли Вид = "КОНСТАНТА" Тогда
		Возврат "Константы";
	ИначеЕсли Вид = "ПОСЛЕДОВАТЕЛЬНОСТЬ" Тогда
		Возврат "Последовательности";
	Иначе
		Возврат Неопределено;
	КонецЕсли;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Шаблоны.

// Шаблон таблицы подключаемых команд.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Вид - Строка
//   * Идентификатор - Строка
//   * Представление - Строка
//   * Подменю - Строка
//   * Важность - Строка
//   * Порядок - Число
//   * Картинка - Картинка
//   * СочетаниеКлавиш - СочетаниеКлавиш
//   * ОтображениеКнопки - Неопределено
//   * ТолькоВоВсехДействиях - Булево
//   * ЗначениеПометки - Строка
//   * ТипПараметра - ОписаниеТипов
//   * ВидимостьВФормах - Строка
//   * Назначение - Строка
//   * ФункциональныеОпции - Строка
//   * УсловияВидимости - Массив
//   * ИзменяетВыбранныеОбъекты - Булево
//   * МножественныйВыбор - Булево, Неопределено
//   * РежимЗаписи - Строка
//   * ТребуетсяРаботаСФайлами - Булево
//   * Менеджер - Строка
//   * Обработчик - Строка
//   * ДополнительныеПараметры - Структура
//   * ИмяФормы - Строка
//   * ПараметрыФормы - Структура, Неопределено
//   * ИмяПараметраФормы - Строка
//   * ПорядокВажности - Число
//   * ИмяВФорме - Строка
//   * ЕстьУсловияВидимости - Булево
//   * КлючПараметровРазмещения - Строка
//
Функция ТаблицаКоманд()
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Вид", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Строка"));
	// Настройки внешнего вида:
	Таблица.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("Подменю", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("Важность", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("Порядок", Новый ОписаниеТипов("Число"));
	Таблица.Колонки.Добавить("Картинка"); // Картинка.
	Таблица.Колонки.Добавить("СочетаниеКлавиш"); // СочетаниеКлавиш.
	Таблица.Колонки.Добавить("ОтображениеКнопки");
	Таблица.Колонки.Добавить("ТолькоВоВсехДействиях", Новый ОписаниеТипов("Булево"));
	Таблица.Колонки.Добавить("ЗначениеПометки", Новый ОписаниеТипов("Строка"));
	// Настройки видимости и доступность:
	Таблица.Колонки.Добавить("ТипПараметра"); // ОписаниеТипов.
	Таблица.Колонки.Добавить("ВидимостьВФормах", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("Назначение", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("ФункциональныеОпции", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("УсловияВидимости", Новый ОписаниеТипов("Массив"));
	Таблица.Колонки.Добавить("ИзменяетВыбранныеОбъекты"); // Булево или Неопределено.
	// Настройки процесса выполнения:
	Таблица.Колонки.Добавить("МножественныйВыбор"); // Булево или Неопределено.
	Таблица.Колонки.Добавить("РежимЗаписи", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("ТребуетсяРаботаСФайлами", Новый ОписаниеТипов("Булево"));
	// Настройки обработчика:
	Таблица.Колонки.Добавить("Менеджер", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("Обработчик", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("ДополнительныеПараметры", Новый ОписаниеТипов("Структура"));
	Таблица.Колонки.Добавить("ИмяФормы", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("ПараметрыФормы"); // Структура или Неопределено.
	Таблица.Колонки.Добавить("ИмяПараметраФормы", Новый ОписаниеТипов("Строка"));
	// Служебные:
	Таблица.Колонки.Добавить("ПорядокВажности", Новый ОписаниеТипов("Число"));
	Таблица.Колонки.Добавить("ИмяВФорме", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("ЕстьУсловияВидимости", Новый ОписаниеТипов("Булево"));
	Таблица.Колонки.Добавить("КлючПараметровРазмещения", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("УсловияВидимостиПоТипамОбъектов", Новый ОписаниеТипов("Соответствие"));
		
	Возврат Таблица;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Прочее.

// Возвращает полное имя подсистемы.
Функция ПолноеИмяПодсистемы() Экспорт
	Возврат "СтандартныеПодсистемы.ПодключаемыеКоманды";
КонецФункции

Функция ОбъединитьТипы(Тип1, Тип2)
	Тип1ЭтоОписаниеТипов = ТипЗнч(Тип1) = Тип("ОписаниеТипов");
	Тип2ЭтоОписаниеТипов = ТипЗнч(Тип2) = Тип("ОписаниеТипов");
	Если Тип1ЭтоОписаниеТипов И Тип1.Типы().Количество() > 0 Тогда
		ИсходноеОписаниеТипов = Тип1;
		ДобавляемыеТипы = ?(Тип2ЭтоОписаниеТипов, Тип2.Типы(), ЗначениеВМассив(Тип2));
	ИначеЕсли Тип2ЭтоОписаниеТипов И Тип2.Типы().Количество() > 0 Тогда
		ИсходноеОписаниеТипов = Тип2;
		ДобавляемыеТипы = ЗначениеВМассив(Тип1);
	ИначеЕсли ТипЗнч(Тип1) <> Тип("Тип") Тогда
		Возврат Тип2;
	ИначеЕсли ТипЗнч(Тип2) <> Тип("Тип") Тогда
		Возврат Тип1;
	Иначе
		Типы = Новый Массив;
		Типы.Добавить(Тип1);
		Типы.Добавить(Тип2);
		Возврат Новый ОписаниеТипов(Типы);
	КонецЕсли;
	Если ДобавляемыеТипы.Количество() = 0 Тогда
		Возврат ИсходноеОписаниеТипов;
	Иначе
		Возврат Новый ОписаниеТипов(ИсходноеОписаниеТипов, ДобавляемыеТипы);
	КонецЕсли;
КонецФункции

Функция ЗначениеВМассив(Значение)
	Результат = Новый Массив;
	Результат.Добавить(Значение);
	Возврат Результат;
КонецФункции

Функция ТребуетсяУказаниеИсточниковКоманд(ПолноеИмяОбъектаМетаданных)
	ВидОбъектаМетаданных = НРег(СтрРазделить(ПолноеИмяОбъектаМетаданных, ".")[0]);
	Возврат ВидОбъектаМетаданных = НРег("ВнешняяОбработка")
		Или ВидОбъектаМетаданных = НРег("ВнешнийОтчет")
		Или ВидОбъектаМетаданных = НРег("Обработка")
		Или ВидОбъектаМетаданных = НРег("Отчет")
		Или ВидОбъектаМетаданных = НРег("ОбщаяФорма");
КонецФункции

Функция ВыражениеВычислениеПометки(Знач ВыражениеПометки)
	Результат = "";
	
	ВыражениеПометки = ВРег(ВыражениеПометки);
	ПутьДоРеквизита = СтрЗаменить(ВыражениеПометки, "НЕ ", "");
	Если СтрНачинаетсяС(ВыражениеПометки, "НЕ ") Тогда
		Результат = "НЕ "	
	КонецЕсли;
	ЧастиПутиЗначениеПометки = СтрРазделить(ПутьДоРеквизита, ".");
	Для Сч = 0 По ЧастиПутиЗначениеПометки.Количество() - 1 Цикл
		ЧастиПутиЗначениеПометки[Сч] = "["""+ЧастиПутиЗначениеПометки[Сч]+"""]";			
	КонецЦикла;
	Результат = Результат + "Форма"+СтрСоединить(ЧастиПутиЗначениеПометки, "");
	
	Возврат Результат;
КонецФункции


#КонецОбласти
